package com.seleniti.enpareja.web.desktop.server.model.integration.service.impl.appengine;

import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;

import org.ioc.commons.utils.jdo.JdoQueryHelper;
import org.ioc.commons.utils.logs.Log4JFormatterLogger;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.common.exception.EnParejaException;
import com.seleniti.enpareja.controller.model.integration.database.domain.Couple;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleRequest;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleRequest.Status;

public class GAEService {

	private static final Log4JFormatterLogger logger = Log4JFormatterLogger.getLogger(GAEService.class);

	PersistenceManager pm;
	private static ThreadLocal<LoggedUserInfo> loggedUserInfoProvider = new ThreadLocal<LoggedUserInfo>();;

	public void setPersistentManager(PersistenceManager persistenceManager) {
		this.pm = persistenceManager;
	}

	public LoggedUserInfo getSessionLoggedUserInfo() {
		return loggedUserInfoProvider.get();
	}

	public void setSessionLoggedUserInfo(LoggedUserInfo loggedUserInfo) {
		loggedUserInfoProvider.set(loggedUserInfo);
	}

	public com.seleniti.enpareja.controller.model.integration.database.domain.User getLoggedUser() {
		UserService userService = UserServiceFactory.getUserService();

		User cu = userService.getCurrentUser();

		if (cu != null) {
			return getUserFromGoogleUser(cu);
		} else {
			LoggedUserInfo lui = this.getSessionLoggedUserInfo();

			if (lui != null && lui.getUserId() != null) {
				return pm.getObjectById(com.seleniti.enpareja.controller.model.integration.database.domain.User.class,
						lui.getUserId());
			}
		}

		return null;
	}

	public LoggedUserInfo getLoggedUserInfo() {

		logger.debug("Getting logged user info...");

		LoggedUserInfo lui = null;

		UserService userService = UserServiceFactory.getUserService();

		User cu = userService.getCurrentUser();

		if (cu != null) {

			lui = new LoggedUserInfo();

			lui.setAdmin(userService.isUserAdmin());

			com.seleniti.enpareja.controller.model.integration.database.domain.User epUser = getUserFromGoogleUser(cu);

			lui.setRequiredExternalLogoutUrl(true);

			fillLoggedUserInfo(epUser, lui);

		} else {
			lui = this.getSessionLoggedUserInfo();

			if (lui != null && lui.getUserId() != null) {
				com.seleniti.enpareja.controller.model.integration.database.domain.User epUser = pm.getObjectById(
						com.seleniti.enpareja.controller.model.integration.database.domain.User.class, lui.getUserId());
				fillLoggedUserInfo(epUser, lui);
			}
		}

		logger.info("... got LoggedUserInfo {0}", lui);

		return lui;
	}

	protected com.seleniti.enpareja.controller.model.integration.database.domain.User getUserFromGoogleUser(User cu) {
		JdoQueryHelper qh = JdoQueryHelper.newQuery(pm,
				com.seleniti.enpareja.controller.model.integration.database.domain.User.class);

		qh.andParams("userEmail==%s", cu.getEmail());
		@SuppressWarnings("unchecked")
		List<com.seleniti.enpareja.controller.model.integration.database.domain.User> found = (List<com.seleniti.enpareja.controller.model.integration.database.domain.User>) qh
				.execute();
		com.seleniti.enpareja.controller.model.integration.database.domain.User epUser = (found != null && !found
				.isEmpty()) ? found.get(0) : null;

		if (epUser == null) {
			epUser = new com.seleniti.enpareja.controller.model.integration.database.domain.User();
			epUser.setUserEmail(cu.getEmail());

			epUser.setNickName(cu.getNickname() != null && !cu.getNickname().isEmpty() ? cu.getNickname() : cu
					.getEmail());
			epUser.setLastAccess(new Date());

			pm.makePersistent(epUser);
		}
		return epUser;
	}

	protected void fillLoggedUserInfo(com.seleniti.enpareja.controller.model.integration.database.domain.User epUser,
			LoggedUserInfo lui) {
		lui.setUserId(epUser.getUserId());
		lui.setUserEmail(epUser.getUserEmail());
		lui.setUserName(epUser.getNickName());
		lui.setGender(epUser.getGender());
		lui.setPasswordSet(epUser.getPassword() != null && !epUser.getPassword().isEmpty());

		Couple couple = null;
		if (epUser.getCouple() != null) {
			couple = lookForCouple(epUser);
			if (couple != null) {
				lui.setCoupleMemberNo(epUser.equals(couple.getMember1()) ? 1 : 2);
			}
		}

		com.seleniti.enpareja.controller.model.integration.database.domain.User userCouple = couple != null ? (epUser
				.equals(couple.getMember1()) ? couple.getMember2() : couple.getMember1()) : null;

		lui.setCoupleId(couple != null ? couple.getCoupleId() : null);
		lui.setCoupleType(couple != null ? couple.getCoupleType() : null);
		lui.setCoupleName(userCouple != null && userCouple.getNickName() != null ? userCouple.getNickName() : null);

		JdoQueryHelper qhFrom = JdoQueryHelper.newQuery(pm, CoupleRequest.class);

		qhFrom.andParams("from==%s", epUser);
		@SuppressWarnings("unchecked")
		List<CoupleRequest> requestsFromYou = (List<CoupleRequest>) qhFrom.execute();
		lui.setSomePendingRequestsFromYou(false);
		lui.setSomeRejectedRequestsFromYou(false);
		for (CoupleRequest cr : requestsFromYou) {
			switch (cr.getStatus()) {
			case PENDING:
				lui.setSomePendingRequestsFromYou(true);
				break;
			case REJECTED:
				lui.setSomeRejectedRequestsFromYou(true);
				break;
			}
		}

		lui.setSomeCoupleRequestsToYou(false);
		if (epUser.getRequestsToYou() != null && !epUser.getRequestsToYou().isEmpty()) {
			for (CoupleRequest cr : epUser.getRequestsToYou()) {
				if (cr.getStatus() == Status.PENDING) {
					lui.setSomeCoupleRequestsToYou(true);
					break;
				}
			}
		}

		lui.setSomeNotifications(epUser.getNotifications() != null && !epUser.getNotifications().isEmpty());
	}

	@SuppressWarnings("unchecked")
	protected Couple lookForCouple(com.seleniti.enpareja.controller.model.integration.database.domain.User epUser) {
		if (epUser.getCouple() != null) {
			JdoQueryHelper qh1 = JdoQueryHelper.newQuery(pm, Couple.class);
			JdoQueryHelper qh2 = JdoQueryHelper.newQuery(pm, Couple.class);

			qh1.openParenthesis();
			qh1.andParams("member1==%s", epUser.getUserId());
			qh1.andParams("member2==%s", epUser.getCouple().getUserId());
			qh1.closeParenthesis();

			qh2.openParenthesis();
			qh2.andParams("member2==%s", epUser.getUserId());
			qh2.andParams("member1==%s", epUser.getCouple().getUserId());
			qh2.closeParenthesis();

			List<Couple> couple1List = (List<Couple>) qh1.execute();
			List<Couple> couple2List = (List<Couple>) qh2.execute();
			Couple couple = couple1List != null && !couple1List.isEmpty() ? couple1List.get(0) : null;
			couple = couple == null && couple2List != null && !couple2List.isEmpty() ? couple2List.get(0) : couple;
			return couple;
		} else {
			return null;
		}
	}

	protected Couple addLoggedUserCoupleFilter(JdoQueryHelper qh) {
		LoggedUserInfo lui = getLoggedUserInfo();
		String coupleId = lui != null ? lui.getCoupleId() : null;
		Couple couple = coupleId != null ? pm.getObjectById(Couple.class, coupleId) : null;

		qh.andParams("couple==%s", couple);

		return couple;
	}

	protected LoggedUserInfo assertLoggedIn(String operationDescription) throws EnParejaException {
		LoggedUserInfo lui = this.getLoggedUserInfo();
		if (lui == null || !lui.isLogged()) {
			String detailedInfo = "You must be logged in to " + operationDescription.toLowerCase();

			logger.error(detailedInfo + ". LUI=" + lui);

			throw new EnParejaException(EnParejaException.ErrorCode.LOGGIN_REQUIRED, detailedInfo);
		}

		return lui;
	}

	protected LoggedUserInfo assertThisLoggedUser(
			com.seleniti.enpareja.controller.model.integration.database.domain.User user, String operationDescription)
			throws EnParejaException {
		LoggedUserInfo lui = assertLoggedIn(operationDescription);

		if (lui != null && !Objects.equal(user.getUserId(), lui.getUserId())) {
			throw new EnParejaException(EnParejaException.ErrorCode.LOGGIN_REQUIRED, operationDescription);
		}

		return lui;
	}

	protected LoggedUserInfo assertThisLoggedCouple(Couple couple, String operationDescription)
			throws EnParejaException {
		LoggedUserInfo lui = assertLoggedIn(operationDescription);

		if (lui != null && (couple.getMember1()==null || !Objects.equal(couple.getMember1().getUserId(), lui.getUserId()))
				&& (couple.getMember2()==null || !Objects.equal(couple.getMember2().getUserId(), lui.getUserId()))) {
			throw new EnParejaException(EnParejaException.ErrorCode.LOGGIN_REQUIRED, operationDescription);
		}

		return lui;
	}
}
